/**
  * Author: Mei
  * Date: 2021-01-09 10:16
  * Description: 线程池
  *
  */
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <iostream>
#include <thread>
#include <condition_variable>
#include <future>
#include <mutex>
#include <memory>
#include <queue>
#include <vector>
#include <functional>

class ThreadPool {
public:
    ThreadPool(size_t);

    template<class F, class ...Args>
    auto
    enqueue(F &&f, Args &&...args) -> std::future<typename std::result_of<F(Args...)>::type>;

    ~ThreadPool();

private:
    std::mutex queue_mutex;
    std::condition_variable condition;
    std::queue<std::function<void()>> tasks; //任务队列
    std::vector<std::thread> workers; //工作线程
    bool stop;
};

//如果任务队列不为空，则取出一个工作线程去执行该任务  反之 则等待
inline ThreadPool::ThreadPool(size_t threads) : stop(false) {
    for (size_t i = 0; i < threads; ++i) {
        workers.emplace_back(
                [this]() {
                    for (;;) {
                        std::function<void()> task;
                        {
                            std::unique_ptr<std::mutex> lock(this->queue_mutex);
                            this->condition.wait(lock, [this] { //条件为false才会等待
                                return this->stop || !this->tasks.empty();
                            });
                            if (this->stop && this->tasks.empty()) {
                                return;;
                            }
                            task = std::move(this->tasks.front()); //取出来的是一个lambda表达式
                            this->tasks.pop();
                        }
                        task(); //执行
                    }
                }
        );
    }

}

//将任务添加进任务队列中
template<class F, class ...Args>
auto
ThreadPool::enqueue(F &&f, Args &&...args) -> std::future<typename std::result_of<F(Args...)>::type> {
    //定义函数的返回值类型
    using result_type = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<result_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args...)));
    std::future<result_type> res = task->get_future();
    {
        std::unique_ptr<std::mutex> lock(queue_mutex);
        if (stop) {
            throw std::runtime_error("enqueue on stopped ThreadPool");
        }
        tasks.emplace([task] {
            (*task)(); //不是异步执行
        });
    }
    condition.notify_one();
    return res;
};

//析构函数
inline ThreadPool::~ThreadPool() {
    {
        std::unique_ptr<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread &work : workers) {
        work.join();
    }
}

#endif